﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;


namespace SSISLib
{
    public partial class CRMConfigForm : Form
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public CRMConfigForm(System.Collections.Hashtable properties, bool bWriteMode, string portID, BlueIntegrator.DAL.DALBase dal)
        {
            InitializeComponent();
            m_bWriteMode = bWriteMode;
            m_portId = portID;
            m_dal = dal;

            int nRelBindings = SafeGetInt(properties["NumRelBindings"]);
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            bf.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
            bf.TypeFormat = System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesWhenNeeded;

            try
            {
                for (int i = 0; i < nRelBindings; i++)
                {
                    string bindingInfo = properties["RelBinding:" + i] as string;

                    if (bindingInfo != null)
                    {
                        System.IO.Compression.GZipStream unzipper = new System.IO.Compression.GZipStream(new System.IO.MemoryStream(System.Convert.FromBase64String(bindingInfo)), System.IO.Compression.CompressionMode.Decompress, true);
                        CRMAPI.Relationship r = (CRMAPI.Relationship)bf.Deserialize(unzipper);
                        m_relationships.Add(r);
                    }
                }

                int nBindings = SafeGetInt(properties["NumTBindings"]);

                for (int i = 0; i < nBindings; i++)
                {
                    string bindingInfo = properties["Binding:" + i] as string;

                    if (bindingInfo != null)
                    {
                        System.IO.Compression.GZipStream unzipper = new System.IO.Compression.GZipStream(new System.IO.MemoryStream(System.Convert.FromBase64String(bindingInfo)), System.IO.Compression.CompressionMode.Decompress, true);
                        CRMAPI.BindingDefn b = (CRMAPI.BindingDefn)bf.Deserialize(unzipper);
                        m_bindings.Add(b);
                    }
                }
            }
            catch
            {
            }

            m_mode = (CRMAPI.WriteMode)SafeGetInt(properties["SPMode"]);

            this.userTextBox.Text = properties["SPUser"] as string ?? "";
            this.urlTextBox.Text = properties["SPURL"] as string ?? "";
            this.orgTextBox.Text = properties["SPOrg"] as string ?? "";
            this.pwdTextBox.Text = BlueIntegrator.Helpers.Encryption.DecryptString(properties["SPPwd"] as string ?? "", "CRMConfig");
            this.timeoutTextBox.Text = SafeGetInt(properties["SPTimeOut"], 100).ToString();

            m_properties = properties;

            if (m_bWriteMode)
            {
                this.captureLabel.Text = this.captureLabel.Text.Replace("capture", "update or filter");
                this.tabControl1.TabPages.Remove(this.Conditions);
                this.modeComboBox.SelectedIndex = 0;

                try
                {
                    this.modeComboBox.SelectedIndex = (int)m_mode + 1;
                }
                catch
                {
                }
            }
            else
            {
                this.modeLabel.Text = "Query Join Mode : ";
                this.modeLabel.Location = new Point(19, this.modeLabel.Location.Y);
                this.modeLabel2.Text = "Specifies whether parent records are only returned where child records exist (Inner Join) or always (Left Join)";
                this.modeComboBox.Items.Clear();
                this.modeComboBox.Items.Add("Inner Join");
                this.modeComboBox.Items.Add("Left Join");
                this.modeComboBox.SelectedIndex = 0;

                if (m_relationships != null && m_relationships.Count > 0)
                {
                    this.modeComboBox.SelectedIndex = m_relationships[0].innerJoin ? 0 : 1;
                }
            }

            // Conditions tab
            this.conditionComboBox.Items.Add("None");
            m_conditionIds.Add((int)CRMAPI.BindingDefn.MappingType.None);

            foreach (int val in System.Enum.GetValues(typeof (CRMAPI.BindingDefn.MappingType)))
            {
                if (val == (int)CRMAPI.BindingDefn.MappingType.None)
                    continue;

                string text = System.Enum.GetName(typeof(CRMAPI.BindingDefn.MappingType), val);

                if (!text.StartsWith("Condition"))
                    continue;

                bool hasParam = false;
                this.conditionComboBox.Items.Add(CRMAPI.GetMappingTypeFriendlyName((CRMAPI.BindingDefn.MappingType)val, out hasParam));

                if (hasParam)
                {
                    m_paramConditionIds.Add(val);
                }

                m_conditionIds.Add(val);
            }

            this.conditionComboBox.SelectedIndex = 0;

            // tree events
            this.fieldTree.BeforeExpand += new TreeViewCancelEventHandler(fieldTree_BeforeExpand);
            this.fieldTree.BeforeCheck += new TreeViewCancelEventHandler(fieldTree_BeforeCheck);
            this.fieldTree.AfterCheck += new TreeViewEventHandler(fieldTree_AfterCheck);
        }

        /// <summary>
        /// Conversion helper
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        private int SafeGetInt(object v)
        {
            return SafeGetInt(v, 0);
        }

        /// <summary>
        /// Conversion helper
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        private int SafeGetInt(object v, int def)
        {
            if (v == null)
                return def;

            try
            {
                return System.Convert.ToInt32(v);
            }
            catch
            {
            }

            return def;
        }
        
        /// <summary>
        /// Property helper
        /// </summary>
        /// <param name="propname"></param>
        /// <returns></returns>
        private object GetPropertyHelper(string propname)
        {
            object val = m_properties[propname];
            return val ?? "";
        }

        /// <summary>
        /// Property helper
        /// </summary>
        /// <param name="propname"></param>
        /// <returns></returns>
        private void SetPropertyHelper(string propname, object val)
        {
            m_properties[propname] = val;
        }

        bool m_bLookup = false;
        System.Collections.Hashtable m_properties = null;
        string m_portId;
        bool m_bWriteMode;
        List<int> m_conditionIds = new List<int>(), m_paramConditionIds = new List<int>();
        List<CRMAPI.BindingDefn> m_bindings = new List<CRMAPI.BindingDefn>();
        List<CRMAPI.Relationship> m_relationships = new List<CRMAPI.Relationship>();
        CRMAPI.WriteMode m_mode = CRMAPI.WriteMode.Upsert;
        bool m_bProcessingCheck = false;
        TabPage m_previousTab = null;
        BlueIntegrator.DAL.DALBase m_dal;

        /// <summary>
        /// Cancel handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Validate and save
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                // complete tab work
                if (this.tabControl1.SelectedTab == this.FieldPage)
                {
                    OnLeaveFieldTab();
                }

                if (modeComboBox.SelectedIndex == 0 && m_bWriteMode)
                {
                    MessageBox.Show("You must select a mode.", "BlueIntegrator", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    this.modeComboBox.Focus();
                    this.tabControl1.SelectedIndex = 0;
                    return;
                }

                if (this.urlTextBox.Text == "")
                {
                    MessageBox.Show("You must enter a CRM server URL.", "BlueIntegrator", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    this.urlTextBox.Focus();
                    this.tabControl1.SelectedIndex = 0;
                    return;
                }

                int timeOut;

                if (!int.TryParse(this.timeoutTextBox.Text, out timeOut))
                    timeOut = 60;

                CRMAPI api = new CRMAPI(this.urlTextBox.Text, this.orgTextBox.Text, this.userTextBox.Text, this.pwdTextBox.Text, timeOut, m_dal);

                try
                {
                    System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
                    api.GetAllEntities();
                }
                catch (System.Exception exc)
                {
                    MessageBox.Show("An error occurred connecting to the CRM server : " + exc.ToString() + "\r\n\r\nPlease check the connection parameters.", "BlueIntegrator", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    this.urlTextBox.Focus();
                    this.tabControl1.SelectedIndex = 0;
                    return;
                }
                finally
                {
                    System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
                }
                this.DialogResult = DialogResult.OK;

                if (this.timeoutTextBox.Text == "")
                    this.timeoutTextBox.Text = "60";

                int i;

                if (!int.TryParse(this.timeoutTextBox.Text, out i))
                {
                    this.timeoutTextBox.Text = "60";
                }

                m_properties["SPMode"] = this.modeComboBox.SelectedIndex - 1;
                m_properties["SPURL"] = this.urlTextBox.Text;
                m_properties["SPOrg"] = this.orgTextBox.Text;
                m_properties["SPUser"] = this.userTextBox.Text;
                m_properties["SPPwd"] = BlueIntegrator.Helpers.Encryption.EncryptString(this.pwdTextBox.Text, "CRMConfig");

                if (!int.TryParse(this.timeoutTextBox.Text, out timeOut))
                    timeOut = 60;

                m_properties["NumTBindings"] = m_bindings.Count;
                m_properties["NumRelBindings"] = m_relationships.Count;

                System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                bf.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
                bf.TypeFormat = System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesWhenNeeded;

                for (i = 0; i < m_bindings.Count; i++)
                {
                    System.IO.MemoryStream str = new System.IO.MemoryStream();
                    System.IO.Compression.GZipStream zipstr = new System.IO.Compression.GZipStream(str, System.IO.Compression.CompressionMode.Compress, true);

                    bf.Serialize(zipstr, m_bindings[i]);
                    zipstr.Flush();
                    zipstr.Close();

                    m_properties["Binding:" + i] = System.Convert.ToBase64String(str.ToArray());
                }

                for (i = 0; i < m_relationships.Count; i++)
                {
                    System.IO.MemoryStream str = new System.IO.MemoryStream();
                    System.IO.Compression.GZipStream zipstr = new System.IO.Compression.GZipStream(str, System.IO.Compression.CompressionMode.Compress, true);

                    bf.Serialize(zipstr, m_relationships[i]);
                    zipstr.Flush();
                    zipstr.Close();

                    m_properties["RelBinding:" + i] = System.Convert.ToBase64String(str.ToArray());
                }

                // set join flag
                if (!m_bWriteMode)
                {
                    foreach (CRMAPI.Relationship r in m_relationships)
                    {
                        r.innerJoin = this.modeComboBox.SelectedIndex == 0;
                    }
                }
                this.DialogResult = DialogResult.OK;
                this.Close();
            }
            catch (System.Exception exc)
            {
                MessageBox.Show("Error : " + exc.ToString(), "BlueIntegrator", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
        }

        /// <summary>
        /// Update conditions enable status
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void conditionsFieldsListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.conditionComboBox.Enabled = this.comparatorComboBox.Enabled = this.conditionsHeaderLabel.Enabled = this.conditionLabel1.Enabled = this.conditionLabel2.Enabled = this.conditionUpdateButton.Enabled = this.conditionsFieldsListView.SelectedIndices.Count == 1;

            if (!this.conditionsHeaderLabel.Enabled)
            {
                this.conditionsHeaderLabel.Text = "Filter condition for selected field";
            }
            else
            {
                this.conditionsHeaderLabel.Text = "Filter condition for " + this.conditionsFieldsListView.SelectedItems[0].Text;
                CRMAPI.BindingDefn bd = this.conditionsFieldsListView.SelectedItems[0].Tag as CRMAPI.BindingDefn;
                this.conditionComboBox.SelectedIndex = m_conditionIds.IndexOf((int)bd.mappingType);
                this.comparatorComboBox.Text = bd.sourceMapping;
            }
        }

        /// <summary>
        /// Update handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void conditionUpdateButton_Click(object sender, EventArgs e)
        {
            CRMAPI.BindingDefn bd = this.conditionsFieldsListView.SelectedItems[0].Tag as CRMAPI.BindingDefn;
            bd.mappingType = (CRMAPI.BindingDefn.MappingType)m_conditionIds[Math.Max(this.conditionComboBox.SelectedIndex,0)];
            bd.sourceMapping = this.comparatorComboBox.Text;

            this.conditionsFieldsListView.SelectedItems[0].SubItems[3].Text = this.conditionComboBox.Items[this.conditionComboBox.SelectedIndex].ToString();
            this.conditionsFieldsListView.SelectedItems[0].SubItems[4].Text = this.comparatorComboBox.Text;
        }

        /// <summary>
        /// Initialisation on tab change
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;

                if (this.tabControl1.SelectedTab != this.FieldPage && m_previousTab == this.FieldPage)
                {
                    OnLeaveFieldTab();
                }
                else if (m_previousTab == this.ConnectionPage || m_previousTab == null)
                {
                    InitialiseTree();
                }
                
                if (this.tabControl1.SelectedTab == this.Conditions)
                {
                    // initialise condition or binding page
                    if (!m_bWriteMode)
                    {
                        this.conditionsFieldsListView.Items.Clear();

                        foreach (CRMAPI.BindingDefn bd in m_bindings)
                        {
                            ListViewItem lvi = this.conditionsFieldsListView.Items.Add(bd.attributeDisplayPath);
                            bool dummy = false;
                            lvi.SubItems.Add(bd.targetName);
                            lvi.SubItems.Add(bd.attributeDefn.ssisType);
                            lvi.SubItems.Add(CRMAPI.GetMappingTypeFriendlyName(bd.mappingType, out dummy));
                            lvi.SubItems.Add(bd.sourceMapping);
                            lvi.Tag = bd;
                        }
                    }
                }
            }
            catch (System.Exception exc)
            {
                MessageBox.Show("Error : " + exc.ToString(), "BlueIntegrator", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
            finally
            {
                System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
            }

            m_previousTab = this.tabControl1.SelectedTab;
        }

        /// <summary>
        /// Field tab completion logic
        /// </summary>
        void OnLeaveFieldTab()
        {
            // was on field tab, no more...
            List<CRMAPI.BindingDefn> newBindings = new List<CRMAPI.BindingDefn>();
            List<CRMAPI.Relationship> relevantRelationships = new List<CRMAPI.Relationship>();

            foreach (TreeNode node in this.fieldTree.Nodes)
            {
                GetTreeBindings(node, newBindings, relevantRelationships, "", "");
            }

            newBindings.Sort();
            m_bindings = newBindings;
            m_relationships = relevantRelationships;
        }

        /// <summary>
        /// Gets the bindings from the field tree
        /// </summary>
        /// <param name="node"></param>
        /// <param name="newBindings"></param>
        private void GetTreeBindings(TreeNode node, List<CRMAPI.BindingDefn> newBindings, List<CRMAPI.Relationship> relevantRelationships, string path, string displaypath)
        {
            if (node.ImageIndex == 4)
            {
                // folder... - handle transparently
                foreach (TreeNode n2 in node.Nodes)
                {
                    GetTreeBindings(n2, newBindings, relevantRelationships, path, displaypath);
                }

                return;
            }

            string id = node.Tag as string;
            string name = node.Text;

            if (id == null && node.Tag is CRMAPI.Relationship)
            {
                id = (node.Tag as CRMAPI.Relationship).relationshipId;
            }
            else if (id == null && node.Tag is CRMAPI.Attribute)
            {
                id = (node.Tag as CRMAPI.Attribute).name;
            }

            string newPath = path + (string.IsNullOrEmpty(path) ? "" : "/") + id;
            string newName = displaypath + (string.IsNullOrEmpty(displaypath) ? "" : "/") + name;

            if ((node.ImageIndex == 2 || node.ImageIndex == 3) && node.Checked)
            {
                CRMAPI.BindingDefn foundBinding = null;

                // a checked attribute - add...
                // first see if already present...
                foreach (CRMAPI.BindingDefn bd in m_bindings)
                {
                    if (bd.attributePath == newPath)
                    {
                        foundBinding = bd;
                        break;
                    }
                }

                if (foundBinding == null)
                {
                    foundBinding = new CRMAPI.BindingDefn();
                    foundBinding.attributeDefn = node.Tag as CRMAPI.Attribute;
                    foundBinding.attributePath = newPath;
                    foundBinding.attributeDisplayPath = newName;
                    foundBinding.targetName = newPath;

                    if (foundBinding.targetName.Contains("/"))
                    {
                        foundBinding.targetName = foundBinding.attributeName;
                    }
                }

                newBindings.Add(foundBinding);

                // also add relationships...
                TreeNode parentNode = node.Parent;

                while (parentNode != null)
                {
                    if (parentNode.Tag is CRMAPI.Relationship)
                    {
                        if (relevantRelationships.IndexOf(parentNode.Tag as CRMAPI.Relationship) < 0)
                            relevantRelationships.Add(parentNode.Tag as CRMAPI.Relationship);
                    }

                    parentNode = parentNode.Parent;
                }
            }

            foreach (TreeNode node2 in node.Nodes)
            {
                GetTreeBindings(node2, newBindings, relevantRelationships, newPath, newName);
            }
        }

        /// <summary>
        /// Tree initialisation
        /// </summary>
        private void InitialiseTree()
        {
            try
            {
                this.fieldTree.Nodes.Clear();
                this.fieldTree.CheckBoxes = true;

                int timeOut;

                if (!int.TryParse(this.timeoutTextBox.Text, out timeOut))
                    timeOut = 60;
                
                CRMAPI api = new CRMAPI(this.urlTextBox.Text, this.orgTextBox.Text, this.userTextBox.Text, this.pwdTextBox.Text, timeOut, m_dal);

                foreach (KeyValuePair<string, string> kvp in api.GetAllEntities())
                {
                    TreeNode node = this.fieldTree.Nodes.Add(kvp.Value);
                    node.Tag = kvp.Key;
                    node.ImageIndex = node.SelectedImageIndex = 0;    // entity
                    TreeNode dummynode = node.Nodes.Add("!!DUMMY!!");
                    node.Collapse();

                    // now populate selected bindings
                    foreach (CRMAPI.BindingDefn bd in m_bindings)
                    {
                        CheckNode(bd.attributeDisplayPath, null);
                    }
                }
            }
            catch (System.Exception exc)
            {
                MessageBox.Show("Error : " + exc.ToString(), "BlueIntegrator", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
        }

        /// <summary>
        /// Node checking on init
        /// </summary>
        /// <param name="path"></param>
        private void CheckNode(string path, TreeNode scope)
        {
            if (string.IsNullOrEmpty(path))
                return;

            string firstPart = path;

            if (path.IndexOf("/") >= 0)
            {
                firstPart = path.Substring(0, path.IndexOf("/"));
                path = path.Substring(firstPart.Length + 1);
            }
            else
            {
                path = null;
            }

            if (scope != null)
            {
                fieldTree_BeforeExpand(this, new TreeViewCancelEventArgs(scope, false, TreeViewAction.Expand));
            }

            foreach (TreeNode tr in (scope == null ? this.fieldTree.Nodes : scope.Nodes))
            {
                if (tr.Text == firstPart)
                {
                    if (string.IsNullOrEmpty(path))
                    {
                        tr.Checked = true;

                        TreeNode tn2 = tr.Parent;

                        while (tn2 != null)
                        {
                            tn2.Expand();
                            tn2 = tn2.Parent;
                        }
                    }
                    else
                    {
                        CheckNode(path, tr);
                    }

                    break;
                }
                else if (tr.Text == "Relationships")
                {
                    bool done = false;

                    foreach (TreeNode tr2 in tr.Nodes)
                    {
                        if (tr2.Text == firstPart)
                        {
                            if (string.IsNullOrEmpty(path))
                            {
                                tr2.Checked = true;
                                tr2.EnsureVisible();
                            }
                            else
                            {
                                CheckNode(path, tr2);
                            }

                            done = true;
                            break;
                        }
                    }

                    if (done)
                        break;
                }
            }
        }

        /// <summary>
        /// Expansion helper
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void fieldTree_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            try
            {
                System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;

                if (e.Node.Nodes.Count == 1 && e.Node.Nodes[0].Text == "!!DUMMY!!")
                {
                    e.Node.Nodes.Clear();
                    int timeOut;

                    if (!int.TryParse(this.timeoutTextBox.Text, out timeOut))
                        timeOut = 60;

                    CRMAPI api = new CRMAPI(this.urlTextBox.Text, this.orgTextBox.Text, this.userTextBox.Text, this.pwdTextBox.Text, timeOut, m_dal);
                    string tag = e.Node.Tag as string;

                    if (tag == null && e.Node.Tag is CRMAPI.Relationship)
                    {
                        tag = (e.Node.Tag as CRMAPI.Relationship).relatedEntity;
                    }

                    // add relationships...
                    TreeNode relnode = e.Node.Nodes.Add("Relationships");
                    relnode.ImageIndex = relnode.SelectedImageIndex = 4;  // folder

                    foreach (CRMAPI.Relationship rel in api.GetRelationships(tag))
                    {
                        TreeNode node = relnode.Nodes.Add(rel.name);
                        node.Tag = rel;
                        node.ImageIndex = node.SelectedImageIndex = 1;    // relationship

                        // add dummy child...
                        TreeNode dummynode = node.Nodes.Add("!!DUMMY!!");
                        node.Collapse();
                    }

                    // and attributes...
                    foreach (CRMAPI.Attribute attr in api.GetAttributes(tag))
                    {
                        TreeNode node = e.Node.Nodes.Add(attr.name);
                        node.Tag = attr;
                        node.ImageIndex = node.SelectedImageIndex = (attr.required ? 3 : 2);    // attribute

                        string attrPath = attr.name;

                        TreeNode parentNode = e.Node;

                        while (parentNode != null)
                        {
                            if (parentNode.Tag is CRMAPI.Relationship)
                            {
                                attrPath = (parentNode.Tag as CRMAPI.Relationship).relationshipId + "/" + attrPath;
                            }
                            else
                            {
                                attrPath = parentNode.Tag + "/" + attrPath;
                            }

                            parentNode = parentNode.Parent;
                        }

                        // see if checked...
                        foreach (CRMAPI.BindingDefn bd in m_bindings)
                        {
                            if (bd.attributePath == attrPath)
                            {
                                node.Checked = true;
                                break;
                            }
                        }
                    }
                }
            }
            catch (System.Exception exc)
            {
                MessageBox.Show("Error : " + exc.ToString(), "BlueIntegrator", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
            finally
            {
                System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
            }
        }

        /// <summary>
        /// Before checking handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void fieldTree_BeforeCheck(object sender, TreeViewCancelEventArgs e)
        {
            if (!m_bProcessingCheck)
            {
                TreeNode topNode = e.Node;

                while (topNode.Parent != null)
                    topNode = topNode.Parent;

                foreach (TreeNode node in this.fieldTree.Nodes)
                {
                    if (node != topNode)
                    {
                        RecursiveUncheck(node);
                    }
                }
            }
        }

        /// <summary>
        /// Hierarchical checking logic
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void fieldTree_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (m_bProcessingCheck)
                return;
            
            m_bProcessingCheck = true;

            try
            {
                if (e.Node.ImageIndex == 2 || e.Node.ImageIndex == 3) // attribute
                {
                    TreeNode parentNode = e.Node.Parent;
                    bool allChecked = false;

                    foreach (TreeNode child in parentNode.Nodes)
                    {
                        allChecked = allChecked && child.Checked;
                    }

                    if (parentNode.Checked != allChecked)
                    {
                        parentNode.Checked = allChecked;
                    }
                }
                else
                {
                    // apply to immediate children
                    foreach (TreeNode child in e.Node.Nodes)
                    {
                        if (!e.Node.Checked)
                        {
                            RecursiveUncheck(child);
                        }
                        else
                            if (child.ImageIndex == 2 || child.ImageIndex == 3)
                            {
                                if (child.Checked != e.Node.Checked)
                                {
                                    child.Checked = e.Node.Checked;
                                }
                            }
                    }
                }
            }
            finally
            {
                m_bProcessingCheck = false;
            }
        }

        /// <summary>
        /// Uncheck helper
        /// </summary>
        /// <param name="node"></param>
        private void RecursiveUncheck(TreeNode node)
        {
            if (node.Checked)
                node.Checked = false;

            foreach (TreeNode child in node.Nodes)
            {
                RecursiveUncheck(child);
            }
        }

        /// <summary>
        /// Nothing to do
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void modeComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
        }

        private void helpButton_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://BlueIntegrator.com/index.aspx?PageId=" + (m_bLookup ? "CMRLookupComponent" : m_bWriteMode ? "CRMDestinationComponent" : "CRMSourceComponent"));
        }

        /// <summary>
        /// Disable param box for no param conditions
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void conditionComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.comparatorComboBox.Enabled = this.conditionComboBox.SelectedIndex < 0 || (!this.conditionComboBox.Items[this.conditionComboBox.SelectedIndex].ToString().Contains("Null") && !this.conditionComboBox.Items[this.conditionComboBox.SelectedIndex].ToString().Contains("This") && !this.conditionComboBox.Items[this.conditionComboBox.SelectedIndex].ToString().Contains("Last") && !this.conditionComboBox.Items[this.conditionComboBox.SelectedIndex].ToString().Contains("Next"));
        }

        /// <summary>
        /// Create Schema Click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            try
            {
                string name = this.urlTextBox.Text + "";

                foreach (var b in m_bindings)
                {
                    name += " , " + b.attributeName;
                }

                CRMEtc.PromptSchemaName psn = new CRMEtc.PromptSchemaName(name);
                string schId = System.Guid.NewGuid().ToString();

                if (psn.ShowDialog() == DialogResult.OK)
                {
                    foreach (KeyValuePair<string, string> s in m_dal.EnumAllSchemaIdsAndNames())
                    {
                        if (s.Value.ToUpper() == psn.textBox1.Text.ToUpper())
                        {
                            schId = s.Key;
                        }
                    }
                    
                    CRMAPI crm = new CRMAPI(this.urlTextBox.Text, this.orgTextBox.Text, this.userTextBox.Text, this.pwdTextBox.Text, 1000, m_dal);
                    // now generate the schema
                    System.Xml.Schema.XmlSchema sch = new System.Xml.Schema.XmlSchema();

                    // add root element
                    System.Xml.Schema.XmlSchemaElement root = new System.Xml.Schema.XmlSchemaElement();
                    root.Name = "BlueIntegratorCRM";
                    sch.Items.Add(root);

                    crm.GenerateSchema(m_bindings.ToArray(), m_relationships.ToArray(), root);

                    // get XSD
                    System.IO.StringWriter sw = new System.IO.StringWriter();
                    sch.Write(sw);

                    // save schema to DAL
                    ///////////////////////
                    m_dal.SaveSchema(schId, sw.ToString(), new BlueIntegrator.DAL.SchemaAuxiliaryProperties());

                    if (m_dal.GetSchemaName(schId) != psn.textBox1.Text)
                        m_dal.RenameSchema(schId, psn.textBox1.Text);
#if DEBUG
                    MessageBox.Show(sw.ToString());
#endif
                }
            }
            catch (System.Exception exc)
            {
                MessageBox.Show("Error : " + exc.ToString(), "BlueIntegrator", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
        }
    }
}
